<?
/**
 * VoiceOne Webservices Methods
 *  
 * @package voiceone_webservices
 * @subpackage mISDN
 */
//******************************************************************************
//******************           PUBLIC FUNCTIONS            *********************
//******************************************************************************
/******************************  ISDN CARDS  **********************************/
/**
* FUNCTION: getIsdnCard
* Get isdn card that match with id. If no id is passed to the function, 
* it returns all cards.
* @param id:int -> Id of the isdn card, all if empty or zero
* @return:IsdnCardArray -> Array of structure contains cards data
*/
function getIsdnCard($id){
  global $db;
  $ret=array();
  //retrieve mISDN cards
  if((isset($id))&&(trim($id)!="")&&($id!=0)) $condition="WHERE id='".$id."'";
  $qry="SELECT * FROM cw_isdn_card $condition ";
  $res=$db->Execute($qry);
  if($res!==false){
  	while($arr=$res->FetchRow()){
    	$ret[]=$arr;
    }
  }
  //return
  return $ret;
}
/**
* FUNCTION: putIsdnCard
* Add an isdn card by simply passing the id of the card type.
* @param id:int -> Id of the isdn card type
* @return:int -> If success returns 1, else returns zero
*/
function putIsdnCard($id,$reload=true) {
  global $db;
  //read how many ports are already installed
  $qry="SELECT * FROM cw_isdn_port";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  $installedPort=$res->RecordCount();
  //read how many ports has the choosed card
  $qry="SELECT ports FROM cw_isdn_card WHERE id='".$id."'";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  $arr=$res->FetchRow();
  $ports=$arr["ports"];
  if((trim($ports)=="")||(strtoupper($ports)=="NULL")||($ports==0)) return 0;
  //read the id of the previous installed card 
  $qry="SELECT MAX(card_id) as lastid, MAX(channel) as lastchannel FROM cw_isdn_port";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  $arr=$res->FetchRow();
  $card_id=$arr["lastid"];
  if((trim($card_id)=="")||(strtoupper($card_id)=="NULL")||($card_id==0)) $card_id=1; else $card_id++;
  $channel=$arr["lastchannel"];
  if((trim($channel)=="")||(strtoupper($channel)=="NULL")||($channel==0)) $channel=1; else $channel++;
  //create card ports entry
	$res=true;
  for($i=1;$i<=$ports;$i++){
    $qry="INSERT INTO cw_isdn_port (port,layermask,protocol,channel,card,card_id) ";
    $qry.="VALUES ('".$i."','te','ptmp','".$channel++."','".$id."','".$card_id."')";
    $res&=$db->Execute($qry);
  }
	if($res===false) return 0;
  //write isdn conf file
	$res=writeMisdnConfFile();
  //return
  if($res!==false) return $card_id;
  return 0;
}
/**
* FUNCTION: delIsdnCard
* Uninstall isdn card that match with id. Id must be passed to the function. 
* @param id:int -> Id of the isdn port
* @return:int -> If success returns 1, else returns zero
*/
function delIsdnCard($id,$reload=true) {
  global $db;
  if((!isset($id))||(trim($id)=="")||($id==0)) return 0;
  if(!isIsdnCardErasable($id)) return 0;
  //delete from db
  $qry="DELETE FROM cw_isdn_port WHERE card_id='".$id."'";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  //rewrite mISDN channels
  $ports=getIsdnPort(0);
  if(is_array($ports)){
    $channel=1;
    foreach($ports as $p){
      $qry="UPDATE `cw_isdn_port` SET `channel`='".$channel."' WHERE `id`='".$p["id"]."'";
      $res=$db->Execute($qry);
      if($res===false) return 0;
      $channel++;
    }
  }
  //write isdn conf file
  if($reload===true){
		$res=writeMisdnConfFile();
  }
  //return
  if($res!==false) return 1;
	return 0;
}
/**
* FUNCTION: isIsdnCardErasable
* Verify if isdn card is erasable. If almost one port of this card or another 
* card that has greater channels has been linked to an exstension or included in a 
* isdn port group, the card cannot be deleted because deleting the card all port number
* should be reloaded
* @param id:int -> Id of the isdn card
* @return:int -> 1 = yes, 0 = no
*/
function isIsdnCardErasable($id) {
  global $db;
  //check input parameter
  if((trim($id)=="")||($id==0)) return 0;
  //get all ports
  $ret=getIsdnPort(0);
  if(is_array($ret)){
    $card_is_next=false;
    foreach($ret as $port){
      if($port["card_id"]==$id) $card_is_next=true;
      if(($card_is_next===true)&&($port["available"]==0)){
        return 0;
      }
    }
  }
  //return erasable
  return 1;
}
/******************************  ISDN PORTS  **********************************/
/**
* FUNCTION: getIsdnPort
* Get isdn port that match with id. If no id is passed to the function, 
* it returns all ports.
* @param id:int -> Id of the isdn port, all if empty or zero
* @return:IsdnPortArray -> Array of structure contains port data
*/
function getIsdnPort($id) {
  global $db;
  //get the port option id
  $portsOptionArr=getOptionByName("ports","mISDN");
  $idPortOption=$portsOptionArr[0]["id"];
	//get port
  $ret=array();
  if((isset($id))&&(trim($id)!="")&&($id!=0)) $condition="WHERE id=$id ";
  $qry="SELECT * FROM cw_isdn_port $condition ORDER BY card_id, port";
  $res=$db->Execute($qry);
  if($res!==false){
    $count=0;
    while($arr=$res->FetchRow()){
      $count++;
      //check if available from isdn port group
      $qry="SELECT * FROM cw_isdn_portgroup WHERE port=".$arr["id"];
      $res2=$db->Execute($qry);
      if(($res2===false)||($res2->RecordCount()>0)) $arr["available"]=0;
      else{
      	$arr["available"]=1;
        //check if available from isdn extension
        $qry="SELECT value ";
        $qry.="FROM cw_client c ";
        $qry.="INNER JOIN cw_option_entity oe ON c.id=oe.entity_id "; 
        $qry.="WHERE oe.option_id='".$idPortOption."' ";
        $qry.="AND c.technology=".MISDN;
        $res2=$db->Execute($qry);
        if($res2!==false){
          while($arrOpt=$res2->FetchRow()){
            $portValue=$arrOpt["value"];
            if($arr["channel"]==$portValue){
            	$arr["available"]=0;
            	break;
            }
          }
        }
      }
      $ret[]=$arr;
    }
  }
  //return
  return $ret;
}
/**
* FUNCTION: updateIsdnPort
* Update isdn port configuration
* @param ports:IsdnPortArray -> Array of structure contains ports data
* @return:int -> If success returns 1, else returns zero
*/
function updateIsdnPort($ports,$reload=true) {
  global $db;
  $res=true;
  if(is_array($ports)){
  	//upload port setting on db
    foreach($ports as $p){
      $id=$p["id"];
      $layermask=$p["layermask"];
      $protocol=$p["protocol"];
      $qry="UPDATE cw_isdn_port SET ";
      $qry.="layermask='".string2sql($layermask)."', ";
      $qry.="protocol='".string2sql($protocol)."' ";
      $qry.="WHERE id='".$id."'";
      $res&=$db->Execute($qry);
    }
    if($res===false) return 0;
    //write isdn conf file
    if($reload===true){
  		$res=writeMisdnConfFile();
    }
    //return
    if($res!==false) return 1;
  }
  return 0;
}
/******************************  ISDN GROUPS  *********************************/
/**
* FUNCTION: getIsdnGroup
* Return isdn group of ports that match with id. If no id is passed to the function, 
* it returns all groups.
* @param id:int -> Id of the isdn group, all if empty or zero
* @return:IsdnGroupArray -> Array of structure contains group data
*/
function getIsdnGroup($id) {
  global $db;
  $count=0;
  //get isdn groups
  $ret=array();
  if((trim($id)!="")&&(trim($id)!="0")&&($id!=0)){
  	$condition="WHERE id='".$id."' ";
  }
  $qry="SELECT * FROM cw_isdn_group $condition";
  $res=$db->Execute($qry);
  if($res!==false){
    while($arr=$res->FetchRow()){ 
      //get channel info
      $channel=getChannel(getChannelFromEntity($arr["id"]));
      if((is_array($channel))&&(is_array($channel[0]))){
      	$arr["speeddial"]=$channel[0]["speeddial"];
      	$arr["channel_order"]=$channel[0]["order"];
        if(is_array($channel[0]["cid"])){
        	$arr["cid"]=$channel[0]["cid"];
        }else{     // @TODO (1) delete else case
          $arr["cid"]=array(
          		"enable_cid" => $channel[0]["enable_cid"],
    					"cid_list" => $channel[0]["cid_list"],
              "cid_number" => $channel[0]["cid_number"],
              "cid_name" => $channel[0]["cid_name"],
              "cid_appexten" => $channel[0]["cid_appexten"],
              "force_cid" => $channel[0]["force_cid"],
              "clir" => $channel[0]["clir"]
          	);
        }
			}else{
      	//should not happen
      	$arr["speeddial"]="";
        $arr["channel_order"]="";
        $arr["cid"]=array();
      }
      //extract the ports of the group
      $port=array();
      $qry="SELECT port FROM cw_isdn_portgroup WHERE `group`='".$arr["id"]."'";
      $res2=$db->Execute($qry);
      if($res2!==false){
      	while($arr2=$res2->FetchRow()){
        	$port[]=$arr2["port"];
        }
      }
      $arr["port"]=$port;
      //extract the phonenumber of the group
      $arr["msns"]=split("\|",$arr["msns"]);
      //read the specific group option
      $arr["option"]=getEntityOption($arr["id"]);
      //set return
      $ret[]=$arr;
    }
  }
  //return
  return $ret;
}
/**
* FUNCTION: putIsdnGroup
* Add an isdn group of ports
* @param group:IsdnGroup -> isdn group structure
* @return:int -> If success returns 1, else returns zero
*/
function putIsdnGroup($group,$reload=true) {
  global $db;
  //if group already exists...
  if((isset($group["id"]))&&($group["id"]!="")){
  	//save group data 
    $old_channel=getChannel(getChannelFromEntity($group["id"]));
    $channel_id=$old_channel[0]["id"];
    $channel_order=$old_channel[0]["order"];
    $entity["id"]=$group["id"];
    //delete group 
    delIsdnGroup($group["id"],false);
  }else{
  	$channel_id="";
    $channel_order="";
  }
  //msns toString
  $msns=msns2string($group["msns"]);
  //extract the rule set id
  $rule=extractRuleFromOption($group["option"],MISDN);
  //create a new entity
  $entity["key"]="0";
  $entity["type"]=MISDN_GROUP;
  $entity_id=putEntity($entity);
  if((is_object($entity_id))||($entity_id==0)) return 0;
  //insert the isdn group
  $qry="INSERT INTO cw_isdn_group (`id`,`name`,`rule`,`msns`,`port_type`,`did`) ";
  $qry.="VALUES ('".$entity_id."','".string2sql($group["name"])."','".$rule."',";
  $qry.="'".string2sql($msns)."','".string2sql($group["port_type"])."','".$group["did"]."')";
  $res=$db->Execute($qry);
  if($res===false) return 0; 
  //save the group specific options 
  $res=putEntityOption($group["option"],MISDN,$entity_id,$group);
  if($res===false) return 0;
  //insert new ports in cw_isdn_portgroup
  if(is_array($group["port"])){
    foreach($group["port"] as $p) {
      $qry="INSERT INTO cw_isdn_portgroup (`port`,`group`) ";
      $qry.="VALUES ('".$p."','".$entity_id."')";
      $res=$db->Execute($qry);
    }
  }
  //insert ports in ast_config
  $portList="";
  $allPorts=getIsdnPort(0);
  if((is_array($allPorts))&&(is_array($group["port"]))){
    foreach($allPorts as $p){
      if(in_array($p["id"],$group["port"],true)) $portList.=$p["channel"].",";
    }
  }
  $portList=substr($portList,0,-1);
  $res=putAstConfig("misdn.conf", $group["name"], "ports", $portList);
  if($res===false) return 0;
  //insert numbers (msns) in ast_config
  $numberList="";
  $msnsArr=split("\|",$msns);
  if(is_array($msnsArr)){
    foreach($msnsArr as $n){
    	//remove the first zero if exists
      if(substr($n,0,1)=="0") $n=substr($n,1); 
      $numberList.=$n.",";
    }
    if(trim($numberList)!="") $numberList=substr($numberList,0,-1);
  }
  if((trim($numberList)=="")||($group["did"]==1)||($group["did"]=="1")){
  	$numberList="*";
  }
  $res=delAstConfig("misdn.conf",$group["name"],"msns"); 
  if($res===false) return 0;
  $res=putAstConfig("misdn.conf", $group["name"], "msns", $numberList);
	if($res===false) return 0;
  //put the new Channel
  $channel=array(
  		"id" =>	$channel_id,
      "order" => $channel_order,
  		"technology" => MISDN,
      "resource" => "g:".$group["name"],
      "key" => $entity_id,
      "msns" => $group["msns"],
      "rule" => $rule,
      "trunk" => 1,
      "did" => $group["did"],
			//"cid" => $group["cid"]      // @TODO (2) decomment and delete others cid parameters
      "cid_list" => $group["cid"]["cid_list"],
      "enable_cid" => $group["cid"]["enable_cid"],
      "cid_number" => $group["cid"]["cid_number"],
      "cid_name" => $group["cid"]["cid_name"],
      "cid_appexten" => $group["cid"]["cid_appexten"],
      "force_cid" => $group["cid"]["force_cid"],
      "clir" => $group["cid"]["clir"]
  	);
  $channelId=putChannel($channel);
  //if cid has been turned off, delete cid settings for the channel
  if($enable_cid==0) delCid($channel["id"],0);
  //update the incoming context
  $res=delAstConfig("misdn.conf", $group["name"], "context");
  if($res===false) return 0;
  $res=putAstConfig("misdn.conf", $group["name"], "context", "trunk_".$channelId); 
  if($res===false) return 0;
  //write the global variables
  $res=generateGlobalVar();
  if($res===false) return 0;
  //if asterisk is ON  
  $status=pbxSwitch("status");
  if(($reload===true)&&($status=="ON")){
    reloadmISDNDriver();
  }
  //set return
  return $entity_id;
}
/**
* FUNCTION: delIsdnGroup
* Delete an isdn group of ports
* @param id:int -> Id of the isdn group
* @return:int -> If success returns 1, else returns zero
*/
function delIsdnGroup($id,$reload=true) {
  global $db;
  //remove the options written in the asterisk table
  $res=delEntityOption($id, MISDN);
  if($res===false) return 0;
  //remove from isdn_group table
  $qry="DELETE FROM cw_isdn_group WHERE id=$id";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  //remove from isdn_portgroup table
  $qry="DELETE FROM cw_isdn_portgroup WHERE `group`=$id";
  $res=$db->Execute($qry);
  if($res===false) return 0;
  //update the Channels
  $channelId=getChannelFromEntity($id);
  if(($reload===true)&&($channelId!=0)) delChannel($channelId,false);
  $ret=delEntity($id);
  //if asterisk is ON reload the chan_misdn
  $status=pbxSwitch("status");
  if(($reload===true)&&($status=="ON")){
    reloadmISDNDriver();
  }
  // set return
  return 1;
}
//******************************************************************************
//*****************           PRIVATE FUNCTIONS            *********************
//******************************************************************************
/**
* PRIVATE FUNCTION: msns2string
* convert an array of msns in a pipe separated string
* @param msns:StringArray -> array of msns numbers
* @return:String -> pipe separated string of number
*/
function msns2string($msns) {
	$msnsString="";
  if(is_array($msns)){ 
    foreach($msns as $m){
    	$m=trim($m);
      if($m!=""){
        if(strpos($m,"-")!==false) {
          $arr=split("-",$m);
          $numberLen=strlen($arr[0]);
          for($i=$arr[0];$i<=$arr[1];$i++){
            $msnsString.=str_pad($i, $numberLen, "0", STR_PAD_LEFT)."|";
          }    
        }else{
        	$msnsString.=$m."|";
        }
      }
    }
    if($msnsString!="") $msnsString=substr($msnsString,0,-1);
	}
  return $msnsString;
}
/**
* PRIVATE FUNCTION: reloadmISDNDriver
* reload chan_misdn in asterisk
* @param null
* @return:null
*/
function reloadmISDNDriver(){
	//reload the chan_misdn
  sendCommand("unload chan_misdn.so");
  sleep(2);
  sendCommand("load chan_misdn.so");
  sleep(2);
  sendCommand("reload");
  sleep(3);
}
/**
* PRIVATE FUNCTION: writeMisdnConfFile
* write mISDN config file
* @param reload:bool -> if true, asterisk and mISDN driver will be reloaded
* @return:bool -> If success method returns true, else it returns false
*/
function writeMisdnConfFile() {
  global $db,$voiceoneWSDocumentRoot,$voTools;
  //file update timestamp
  $update=date("r");
  //process all isdn ports
  $driverArr=array();
  $cardsArr=array();
  $ports=getIsdnPort(0);
  if((is_array($ports))&&(count($ports)>0)){
  	$old_card_id="";
    foreach($ports as $p){
    	//extract port params
      $card_id=$p["card_id"];
      $card_type=$p["card"];
      $port_number=$p["port"];
      $layermask=$p["layermask"]; 
      $protocol=$p["protocol"];
      //if new card...
      if($card_id!=$old_card_id){
      	if($old_card_id!=""){
        	//save old card into array
        	$cardsArr[]=array(
        		"name" => $card_name,
            "ports" => $portsArr
        	);
        }
        $portsArr=array();
        //retrieve name and driver
      	$card=getIsdnCard($card_type);
        if((!is_array($card))||(!is_array($card[0]))) return false;
        $card_name=$card[0]["name"];
        $card_driver=$card[0]["driver"];
        //add drivers to configuration
        $card_driverArr=split("\|",$card_driver);
        if(!is_array($card_driverArr)) return false;
        foreach($card_driverArr as $d){
        	if((is_array($driverArr))&&(!in_array(trim($d),$driverArr,true))){
        		$driverArr[trim($d)]=array(
            		"name" => trim($d)
            	);
          }
        }
        //change old card
        $old_card_id=$card_id;
      }
      //create card's ports array
      $portsArr[$port_number]=array(
      		"layermask" => $layermask,
          "protocol" => $protocol
      	);
    }
    //save the last card
    $cardsArr[]=array(
    		"name" => $card_name,
        "ports" => $portsArr
    	);
  }
  //create XML  
  $EOF="\n";
  $xml="<?xml version=\"1.0\"?>".$EOF;
  $xml.="<!--".$EOF."- - - - - - - - - - - - - - - - - - - - - - - - -".$EOF;
  $xml.="This file is automatic generated by VoiceOne.".$EOF;
  $xml.="If mISDN configuration is reloaded in VoiceOne,".$EOF."manual edits of this file will be overwritten".$EOF;
  $xml.="Date: ".$update.$EOF;
  $xml.="- - - - - - - - - - - - - - - - - - - - - - - - -".$EOF."-->".$EOF;
  $xml.="<mISDNconf>".$EOF;
  if(is_array($driverArr)){
  	foreach($driverArr as $d){
    	$xml.="  <module>".$d["name"]."</module>".$EOF;
    }
  }
  $xml.="  <devnode user=\"root\" group=\"root\" mode=\"644\">mISDN</devnode>".$EOF;
  if(is_array($cardsArr)){
  	foreach($cardsArr as $c){
    	$xml.="  <card type=\"".$c["name"]."\">".$EOF;
      if(is_array($cardsArr)){
      	foreach($c["ports"] as $n=>$p){
       	 	$xml.="    <port mode=\"".$p["layermask"]."\" link=\"".$p["protocol"]."\">".$n."</port>".$EOF;
        }
      }
      $xml.="  </card>".$EOF;
    }
  }
  $xml.="</mISDNconf>".$EOF;
  //write conf file in sandbox
	$filename=$voiceoneWSDocumentRoot."/sandbox/mISDN.conf";  
  $ret=@file_put_contents($filename,$xml); 
	if($ret===false) return false;
  //move conf file from sandbox to system conf dir
  exec("sudo $voTools misdnWriteConf $filename",$out);
  //turn off asterisk
  pbxSwitch("stop");
  //restart mISDN and wait it goes up
  exec("sudo $voTools misdn_init restart",$out);
  sleep(3);
	//return
  return true;
}
?>